#!/usr/bin/env python

# PyQt modules
from PyQt4.QtCore import *
from PyQt4.QtNetwork import *

# Our modules
import CryptManager
import Message

# Standard modules
import time
import sys
import string
import re

# More to add here.  Also, this isn't an immutable object, so
# is a bit slower than I would wish.
highLevelStatus = {"Login"	: 1,
						 "None"	: 2}

lowLevelStatus = {"GenKey"		: 1,
						"GenInfo"	: 2,
						"Idle"		: 3,
						"None" 		: 4}

class HubConnection(QObject):
	def __init__(self, config, app):

		QObject.__init__(self)
		# Create the tcp socket and add some signals
		self.__socket = QTcpSocket(self)
		QObject.connect(self.__socket, SIGNAL("connected()"), self.__hubConnected)
		QObject.connect(self.__socket, SIGNAL("disconnected()"), self.__hubDisconnected)
		QObject.connect(self.__socket, SIGNAL("readyRead()"), self.__hubRead)
		QObject.connect(self.__socket, SIGNAL("error()"), self.__hubError)

		# We aren't connected by default
		self.__connected = False

		# Store a reference to some config stuff
		self.__config = config

		# Current high-level status
		self.__highStatus = highLevelStatus["None"]
		self.__lowStatus = lowLevelStatus["None"]

		# Reference to our cryptomanager
		self.__crypt = CryptManager.CryptManager()

		# XXX REMOVE XXX */
		self.__app = app

	def connected(self):
		print (self.__socket.state())
		print (self.__socket.error())
		return self.__connected

	# Do some stuff and do the key generation/whatnot in later callbacks
	def login(self, ip, port):
		if self.__connected == True:
			return

		self.__ip = ip
		self.__port = port

		# Connect to the server
		self.__socket.connectToHost(ip, port)

		# Create a timer to catch timeouts
		self.__conn_timer = QTimer(self)
		QObject.connect(self.__conn_timer, SIGNAL("timeout()"), self.__hubError)
		self.__conn_timer.start(5000);

		self.__highStatus = highLevelStatus["Login"]
		self.__lowStatus = lowLevelStatus["GenKey"]

	def publicMessage(self, msg):
		if self.__connected == False:
			return

		# Generate the data to send to the client
		stream = "<" + self.__config.nick + "> " + msg
		self.__socket.write(stream)
		print ("Send hub message: " + stream)

	def privateMessage(self, msg, nick):
		if self.__connected == False:
			return

		# Generate the data to send to the client
		stream = "$To: " + nick + " From: " + self.__config.nick \
					+ " $<" + self.__config.nick + "> ;" + msg

		self.__socket.write(stream)
		print ("Sent private message: " + stream)

		# Message objects used for outgoing stuff as well?

	def clientConnect(self, nick):
		print ("Connect to client")

	def logout(self):
		if self.__connected == False:
			return
		print ("Logout")

	def __hubConnected(self):
		print ("__hubConnected")
		self.__connected = True
		# Not sure if this is correct
		self.__conn_timer.deleteLater()
		self.__conn_timer = None

	def __hubDisconnected(self):
		self.__connected = False
		print ("__hubDisconnected")
	
	def __hubRead(self):
		# Basically, we have to parse this for '|', and store
		# anything left over for the next packet.
		msg = str(self.__socket.readAll())
		arr = msg.split("|")

		for cmd in arr:
			print cmd
			if self.__highStatus == highLevelStatus["Login"]:
				self.__hubConnectHandler(cmd)

	def __hubError(self):
		# Shiiiiiiit...
		print("Error!")
		self.__app.exit()
	
	# Maybe this should be checking for commands first, then inside that if
	# Check the low-level status, etc.
	def __hubConnectHandler(self, msg):
		print ("__hubConnectHandler")
		msg_split = msg.split(" "); # We can probably split every other message
		if self.__lowStatus == lowLevelStatus["GenKey"]:

			# sometimes there's stuff before $Lock
			msg_lock = re.match('^(.*)Lock (.*) Pk', msg)

			# msg_split[0] == "$Lock": <-- Not needed, the regex already checks
			# if the command contains $Lock
			if msg_lock != "None":

				# Create the key command
				cmd = Message.Message(self.__crypt.lockToKey(msg_lock.group(2)))
				cmd.keyFormat("Key")

				# Write the key command
				self.__socket.writeData(cmd.msg)

				# Generate and write the validate nick command
				cmd = Message.Message("")
				cmd.commandAppendCheck(self.__config.nick)
				cmd.commandFormat("ValidateNick")
				cmd.commandPrint()
				self.__socket.writeData(bytes(cmd.msg))

				# Remove the used commands
				self.__lowStatus = lowLevelStatus["GenInfo"]

			elif msg_lock == "None":
				print ("Unrecognized command in sequence 'GenKey'", msg)

		elif self.__lowStatus == lowLevelStatus["GenInfo"]:
			# Send our information
			if msg_split[0] == "$Hello":
				cmd = Message.Message("")
				cmd.commandAppendCheck("{0} {1} {2}"
									.format(self.__config.nick,
											self.__config.description,
											self.__config.clientTag))
				cmd.commandAppend("$ $1000$")
				cmd.commandAppendCheck("{0}"
									.format(self.__config.mailAddress))
				cmd.commandAppend("$")
				cmd.commandAppendCheck("{0}"
									.format(self.__config.shareSize))
				cmd.commandAppend("$")
				cmd.commandFormat("MyINFO $ALL")
				cmd.commandPrint()
				self.__socket.writeData(bytes(cmd.msg))
				self.__lowStatus = lowLevelStatus["Idle"]

			# Apparently we just resend the previous command here...
			elif msg_split[0] == "$ValidateDenide":
				print ("DENIIIIIED BIIITCH")

			# This needs to go to gui
			elif msg_split[0] == "$HubName":
				print ("Hub name:", msg_split[1])

			elif msg != "":
				print ("Unrecognized command in sequence 'GenInfo'", msg)

		else:
			print (msg)

##############################
# XXX ALL BELOW IS TESTING XXX
##############################

class Config():
	def __init__(self):
		self.nick = "AeroDC2"
		self.description = "OMGL$LWTF"
		self.mailAddress = ""
		self.shareSize = "80591620717411303424"
		self.clientTag = "<AeroDC: definitely the shit>"
		self.hubAddress = "uwdc.circleofcurrent.com"
		self.hubPort = 411
#		self.hubAddress = "localhost"
#		self.hubPort = 4111

# Dirty hack so we can use ctrl-c to die.
import signal
signal.signal(signal.SIGINT, signal.SIG_DFL)
# End

app = QCoreApplication(sys.argv)
config = Config()
test = HubConnection(config, app)
test.login(config.hubAddress, config.hubPort)

app.exec_()

